package handler

import (
	"bytes"
	"errors"
	"fmt"
	"github.com/mitchellh/mapstructure"
	"google.golang.org/protobuf/proto"
	"log"
	"net"
	"src/config"
	"src/connection"
	"src/global"
	"src/model"
	"src/model/ProtoModel"
	"src/service"
	"src/task"
	"src/utils"
	"strconv"
)

// subscribe 订阅逻辑
type TcpHandler struct {
}

func NewTcpHandler() *TcpHandler {
	return &TcpHandler{}
}
func createServer(ip string, port int) {

}
func (this *TcpHandler) TcpHandler(ip string, port int) {

	var sport string = strconv.Itoa(port)
	//
	// 1. 建立tcp连接监听通道
	listen, err := net.Listen("tcp", ip+":"+sport)
	if err != nil {
		panic(err)
	}
	// 3. 关闭监听通道
	defer listen.Close()
	log.Println("server is Listening")
	for {
		// 2. 进行通道监听
		tcpconn, err := listen.Accept()
		if err != nil {
			continue
		}
		go this.TcpHandler2(tcpconn)
	}
}

// 用户发起请求触发 ws链接 绑定触发
func (this *TcpHandler) TcpHandler2(tcpconn net.Conn) {

	clientIP := tcpconn.RemoteAddr().String()
	log.Println("==> accepted new client, IP:", clientIP)
	//fmt.Println("r.URL", r.URL.Host)
	//ip 请求token授权连续失败 禁止链接
	if (&utils.Common{}).TokenIpWall(clientIP) == false {
		//w.Write([]byte("wall"))
		log.Println("ip连续异常拦截")
		return
	}

	conn, errc := (&connection.Tcp{}).GetConn(&tcpconn)
	if errc != nil {
		return
	}
	//唯一ID自增
	tcpCli := global.Tcp_.Inc()
	Id := strconv.FormatInt(tcpCli.Id, 10)
	conn.Id = Id
	//建立全局缓存
	global.Tcp_.ConnList[Id] = conn
	this.onRequest(conn)

	// 启动一个协程去单独处理该连接
	//go handleRequest(conn)
}

// websocket 请求分发
func (this *TcpHandler) onRequest(conn *connection.Tcp) error {

	var err error
	defer func() {
		if err != nil {
			//清除链接状态 以及全局缓存数据
			(&task.Purification{}).CleanConStateTcp(conn.Id)
		}
		//conn.Close()
	}()
	for {
		msgbytes, err := conn.ReadMess()

		if err != nil {
			log.Println(err)
			break
		}
		buffer1 := bytes.NewBuffer(msgbytes)
		//更新心跳检测时间
		conn.UpdateTime()
		unreadBytes, _ := buffer1.ReadByte()
		dataType := ProtoModel.DataTypeProto(unreadBytes)
		switch dataType {
		case ProtoModel.DataTypeProto_PONG:
			fmt.Println(msgbytes)
			fmt.Println(string(buffer1.Bytes()))
			//解码
			//tb := &ProtoModel.TransportableProto{}
			//var act uint8 = 1
			//toMsg := bytes.NewBuffer([]byte{uint8(ProtoModel.DataTypeProto_PING.Number())})
			//toMsg.Write([]byte(ProtoModel.DataTypeProto_PING.String()))
			//SendMsgToConn(conn1, toMsg.Bytes())
		case ProtoModel.DataTypeProto_PING:
		case ProtoModel.DataTypeProto_MESSAGE:
			err = this.onMessage(buffer1.Bytes(), conn)
			if err != nil {
				return err
			}
		case ProtoModel.DataTypeProto_SENT:
			err = this.onSent(buffer1.Bytes(), conn)
			if err != nil {
				return err
			}
		case ProtoModel.DataTypeProto_REPLY:
		default:

		}
		if err != nil {
			return err
		}
	}
	return nil
}

func (*TcpHandler) onMessage(bodys []byte, conn *connection.Tcp) error {

	return nil
}
func (*TcpHandler) onPONG(bodys []byte, conn *connection.Tcp) error {
	return nil
}
func (*TcpHandler) onReply(bodys []byte, conn *connection.Tcp) error {
	return nil
}

func (*TcpHandler) onSent(bodys []byte, conn *connection.Tcp) error {
	sendbody := &ProtoModel.SendBodyProto{}
	err := proto.Unmarshal(bodys, sendbody)
	if err != nil && sendbody.Key == "" {
		//fmt.Println(sendbody)
		return err
		//fmt.Println(ProtoModel.DataTypeProto(tb.Type[0]))
		//fmt.Println(tb.Body)
		//fmt.Printf("%T", tb)
	}
	switch sendbody.GetKey() {
	//登录鉴权
	case "client_bind":
		var err error
		reply := &ProtoModel.ReplyBodyProto{
			Key:  "client_bind",
			Code: "200",
			Data: make(map[string]string),
		}
		defer func() {
			if err != nil {
				//绑定失败返回前端
				ret := bytes.Buffer{}
				reply.Code = "401"
				reply.Data["msg"] = "绑定失败"
				data, _ := proto.Marshal(reply)
				ret.WriteByte(byte(ProtoModel.DataTypeProto_REPLY.Number()))
				ret.Write(data)
				conn.WriteMes(ret.Bytes())
			}
		}()
		//ClientBindProto
		clientBind := model.ClientBindProto{}
		err = mapstructure.Decode(sendbody.Data, &clientBind)
		if err != nil {
			return err
		}
		// 登录鉴权
		if clientBind.Token == "" {
			return errors.New("token不能存在")
		}
		auth := &service.Auth{}
		userinfo, err := auth.JwtAuth(clientBind.Token)
		if err != nil {

			return err
		}
		//仅限制一个设备在线
		if config.V().GetBool("online.only-one-online") {
			wscon, err := auth.CheckUidDid(userinfo.Uid, clientBind.DeviceId)
			if err != nil {
				//设备冲突是否关闭的新链接
				if config.V().GetInt("conflict-close-type") == 1 {
					conn.Close()
					return err
				}
				wscon.Close()
				//return err
			}
		} else {
			wscon, err := auth.CheckUidDid(userinfo.Uid, clientBind.DeviceId)
			if err != nil {
				//设备冲突是否关闭的新链接
				if config.V().GetInt("conflict-close-type") == 1 {
					conn.Close()
					return err
				}
				//关闭已在线的链接
				wscon.Close()

				//return err
			}
		}
		//fmt.Println("jwtAuth(clientBind.Token) uid:", userinfo.Uid)
		//conn.Uid = new(string)
		//conn.Uid=userinfo.Uid
		conn.User.AppVersion = clientBind.AppVersion
		conn.User.Channel = clientBind.Channel
		conn.User.DeviceName = clientBind.DeviceName
		conn.User.DeviceId = clientBind.DeviceId
		conn.User.OsVersion = clientBind.OsVersion
		conn.User.Language = clientBind.Language
		conn.Did = clientBind.DeviceId
		//conn.User.
		//global.Tcp_.ConnList[global.Inc().Id]=conn
		var uid = userinfo.Uid
		conn.Uid = uid
		userList := global.Tcp_.UserList
		userList[uid] = append(userList[uid][:], conn.Id)

		global.Tcp_.ConnList[conn.Id] = conn

		////删除未登录的记录
		//delete(global.Tcp_.ConnList, conn.Id)
		reply.Code = "200"
		data, _ := proto.Marshal(reply)
		//授权绑定成功 发送消息给用户
		//buf := bytes.NewBuffer([]byte{uint8(ProtoModel.DataTypeProto_REPLY.Number())})
		//buf.Write(data)
		//err = conn.WriteMes(buf.Bytes())
		(&service.ReplyBody{}).Send(data, conn.Id)
		if err != nil {
			return err
		}
		return nil
		//房间绑定
	case "room_bind":
		var err error
		reply := &ProtoModel.ReplyBodyProto{
			Key:  "room_bind",
			Code: "200",
			Data: make(map[string]string),
		}
		defer func() {
			if err != nil {
				//绑定失败返回前端
				ret := bytes.Buffer{}
				reply.Code = "401"
				reply.Data["msg"] = "绑定失败"
				data, _ := proto.Marshal(reply)
				ret.WriteByte(byte(ProtoModel.DataTypeProto_REPLY.Number()))
				ret.Write(data)
				conn.WriteMes(ret.Bytes())
			}
		}()
		//ClientBindProto
		roomBind := model.RoomBindModel{}
		err = mapstructure.Decode(sendbody.Data, &roomBind)
		if err != nil {
			return err
		}
		// 登录鉴权
		if roomBind.Token == "" {
			return errors.New("token不能存在")
		}
		auth := &service.Auth{}
		userinfo, err := auth.RoomBindAuthTcp(&roomBind, conn)
		if err != nil {

			return err
		}

		////删除未登录的记录
		//delete(global.Ws_.ConnList, conn.Id)
		reply.Code = "200"
		reply.Data["msg"] = userinfo.Nickname + "进入房间"
		reply.Data["uid"] = userinfo.Uid
		data, _ := proto.Marshal(reply)
		//授权绑定成功 发送消息给用户
		//buf := bytes.NewBuffer([]byte{uint8(ProtoModel.DataTypeProto_REPLY.Number())})
		//buf.Write(data)
		//err = conn.WriteMes(buf.Bytes())
		(&service.ReplyBody{}).Send(data, conn.Id)
		if err != nil {
			return err
		}
		return nil
	default:

	}
	return nil
}
